﻿// Copyright 2009 John Diss (www.newgrove.com)
/*
 * This license governs use of the accompanying software. If you use the software, you
 * accept this license. If you do not accept the license, do not use the software.
 * 
 * 1. Definitions
 * The terms "reproduce," "reproduction," "derivative works," and "distribution" have the
 * same meaning here as under U.S. copyright law.
 * A "contribution" is the original software, or any additions or changes to the software.
 * A "contributor" is any person that distributes its contribution under this license.
 * "Licensed patents" are a contributor's patent claims that read directly on its contribution.
 * 
 * 2. Grant of Rights
 * (A) Copyright Grant- Subject to the terms of this license, including the license conditions 
 *     and limitations in section 3, each contributor grants you a non-exclusive, worldwide, 
 *     royalty-free copyright license to reproduce its contribution, prepare derivative works 
 *     of its contribution, and distribute its contribution or any derivative works that you create.
 * (B) Patent Grant- Subject to the terms of this license, including the license conditions and 
 *     limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free 
 *     license under its licensed patents to make, have made, use, sell, offer for sale, import, 
 *     and/or otherwise dispose of its contribution in the software or derivative works of the 
 *     contribution in the software.
 * 
 * 3. Conditions and Limitations
 * (A) No Trademark License- This license does not grant you rights to use any contributors' 
 *     name, logo, or trademarks.
 * (B) If you bring a patent claim against any contributor over patents that you claim are 
 *     infringed by the software, your patent license from such contributor to the software 
 *     ends automatically.
 * (C) If you distribute any portion of the software, you must retain all copyright, patent, 
 *     trademark, and attribution notices that are present in the software.
 * (D) If you distribute any portion of the software in source code form, you may do so only under 
 *     this license by including a complete copy of this license with your distribution. If you 
 *     distribute any portion of the software in compiled or object code form, you may only do so 
 *     under a license that complies with this license.
 * (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no 
 *     express warranties, guarantees or conditions. You may have additional consumer rights under 
 *     your local laws which this license cannot change. To the extent permitted under your local laws, 
 *     the contributors exclude the implied warranties of merchantability, fitness for a particular 
 *     purpose and non-infringement.
 * 
 */
using System;
using System.Collections.Generic;
using NPack.Numeric.Interfaces;

namespace NPack.Numeric
{
    [Serializable]
    public struct Complex<T> : INumeric<Complex<T>>
    {
        public readonly T Imaginary;
        public readonly T Real;

        public Complex(T real, T imaginary)
        {
            Real = real;
            Imaginary = imaginary;
        }

        #region INumeric<Complex<T>> Members

        public Complex<T> Add(Complex<T> other)
        {
            return this + other;
        }

        public Complex<T> Subtract(Complex<T> other)
        {
            return this - other;
        }

        public Complex<T> Multiply(Complex<T> other)
        {
            return this*other;
        }

        public Complex<T> Divide(Complex<T> other)
        {
            return this/other;
        }

        public bool Equals(Complex<T> other)
        {
            return Equals(Real, other.Real) && Equals(Imaginary, other.Imaginary);
        }

        public int CompareTo(Complex<T> other)
        {
            Int32 v = Comparer<T>.Default.Compare(Real, other.Real);
            if (v == 0)
                return Comparer<T>.Default.Compare(Imaginary, other.Imaginary);
            return v;
        }

        #endregion

        public override int GetHashCode()
        {
            return Real.GetHashCode() ^ Imaginary.GetHashCode();
        }

        public static Complex<T> operator +(Complex<T> l, Complex<T> r)
        {
            return new Complex<T>(
                NumericOperations<T, T, T, Real<T>>.Add(l.Real, r.Real),
                NumericOperations<T, T, T, Real<T>>.Add(l.Imaginary, r.Imaginary));
        }

        public static Complex<T> operator -(Complex<T> l, Complex<T> r)
        {
            return new Complex<T>(
                NumericOperations<T, T, T, Real<T>>.Sub(l.Real, r.Real),
                NumericOperations<T, T, T, Real<T>>.Sub(l.Imaginary, r.Imaginary));
        }

        public static Complex<T> operator *(Complex<T> l, Complex<T> r)
        {
            return new Complex<T>(
                NumericOperations<T, T, T, Real<T>>.Mul(l.Real, r.Real),
                NumericOperations<T, T, T, Real<T>>.Mul(l.Imaginary, r.Imaginary));
        }

        public static Complex<T> operator /(Complex<T> l, Complex<T> r)
        {
            return new Complex<T>(
                NumericOperations<T, T, T, Real<T>>.Div(l.Real, r.Real),
                NumericOperations<T, T, T, Real<T>>.Div(l.Imaginary, r.Imaginary));
        }


        public static Complex<T> operator +(Complex<T> l, Real<T> r)
        {
            return new Complex<T>(
                NumericOperations<T, T, T, Real<T>>.Add(l.Real, r.Value),
                l.Imaginary);
        }

        public static Complex<T> operator -(Complex<T> l, Real<T> r)
        {
            return new Complex<T>(
                NumericOperations<T, T, T, Real<T>>.Sub(l.Real, r.Value),
                l.Imaginary);
        }

        public static Complex<T> operator *(Complex<T> l, Real<T> r)
        {
            return new Complex<T>(
                NumericOperations<T, T, T, Real<T>>.Mul(l.Real, r.Value),
                l.Imaginary);
        }

        public static Complex<T> operator /(Complex<T> l, Real<T> r)
        {
            return new Complex<T>(
                NumericOperations<T, T, T, Real<T>>.Div(l.Real, r.Value),
                l.Imaginary);
        }


        public static Complex<T> operator +(Complex<T> l, T r)
        {
            return new Complex<T>(
                NumericOperations<T, T, T, Real<T>>.Add(l.Real, r),
                l.Imaginary);
        }

        public static Complex<T> operator -(Complex<T> l, T r)
        {
            return new Complex<T>(
                NumericOperations<T, T, T, Real<T>>.Sub(l.Real, r),
                l.Imaginary);
        }

        public static Complex<T> operator *(Complex<T> l, T r)
        {
            return new Complex<T>(
                NumericOperations<T, T, T, Real<T>>.Mul(l.Real, r),
                l.Imaginary);
        }

        public static Complex<T> operator /(Complex<T> l, T r)
        {
            return new Complex<T>(
                NumericOperations<T, T, T, Real<T>>.Div(l.Real, r),
                l.Imaginary);
        }

        public static bool operator ==(Complex<T> l, Complex<T> r)
        {
            return l.Equals(r);
        }

        public static bool operator !=(Complex<T> l, Complex<T> r)
        {
            return !(l == r);
        }

        public static implicit operator Real<T>(Complex<T> c)
        {
            return new Real<T>(c.Real);
        }

        #region IExponential<Complex<T>> Members

        public Complex<T> Power(double exponent)
        {
            throw new NotImplementedException();
        }

        public Complex<T> Sqrt()
        {
            throw new NotImplementedException();
        }

        public Complex<T> Log(double newBase)
        {
            throw new NotImplementedException();
        }

        public Complex<T> Log()
        {
            throw new NotImplementedException();
        }

        public Complex<T> Exp()
        {
            throw new NotImplementedException();
        }

        #endregion

        #region IHasOne<Complex<T>> Members

        public Complex<T> One
        {
            get { throw new NotImplementedException(); }
        }

        #endregion

        #region IHasZero<Complex<T>> Members

        public Complex<T> Zero
        {
            get { throw new NotImplementedException(); }
        }

        #endregion

        #region INegatable<Complex<T>> Members

        public Complex<T> Negative()
        {
            throw new NotImplementedException();
        }

        #endregion
    }
}